package com.hrg.rds.infra.executor;

import cn.hutool.core.collection.CollUtil;
import com.hrg.rds.biz.waybill.domain.instance.CmdInstanceVo;
import com.hrg.rds.biz.waybill.domain.instance.WaybillInstanceStatus;
import com.hrg.rds.biz.waybill.domain.instance.WaybillInstanceVo;
import com.hrg.rds.biz.waybill.domain.template.cmd.CmdParam;
import com.hrg.rds.biz.waybill.domain.template.cmd.CmdParamType;
import com.hrg.rds.biz.waybill.domain.template.cmd.CmdType;
import com.hrg.rds.infra.async.wrapper.WorkerWrapper;

import java.util.*;

public class ExecutorUtil {

	public static WorkerWrapper<?,?>[] wrapWorker(WaybillInstanceVo waybillInstanceVo) {
		CmdInstanceVo rootCmdInstanceVo = waybillInstanceVo.getRootCmdInstanceVo();
		String waybillInstanceId = waybillInstanceVo.getId();

		if (waybillInstanceVo.getWaybillInstanceStatus().equals(WaybillInstanceStatus.ToBeDispatched)) {
			// StartCmd
			CmdInstanceWorker startWorker = new CmdInstanceWorker(null, CmdType.StartCmd, CmdInstanceContext.builder().waybillInstanceId(waybillInstanceId).build().setWaybillInstanceVo(waybillInstanceVo));
			// JoinCmd
			CmdInstanceWorker joinWorker = new CmdInstanceWorker(null, CmdType.JoinCmd, CmdInstanceContext.builder().waybillInstanceId(waybillInstanceId).build().setWaybillInstanceVo(waybillInstanceVo));
			WorkerWrapper<List<CmdParam>, List<CmdParam>> joinWorkerWrapper = new WorkerWrapper.Builder<List<CmdParam>, List<CmdParam>>()
					.worker(joinWorker)
					.callback(joinWorker)
					.build();
			return new WorkerWrapper[]{ new WorkerWrapper.Builder<List<CmdParam>, List<CmdParam>>()
					.worker(startWorker)
					.callback(startWorker)
					.next(joinWorkerWrapper)
					.build() };
		} else if (waybillInstanceVo.getWaybillInstanceStatus().equals(WaybillInstanceStatus.Executing)) {

			CmdInstanceWorker joinWorker = new CmdInstanceWorker(null, CmdType.JoinCmd, CmdInstanceContext.builder().waybillInstanceId(waybillInstanceId).build().setWaybillInstanceVo(waybillInstanceVo));
			WorkerWrapper<List<CmdParam>, List<CmdParam>> joinWorkerWrapper = new WorkerWrapper.Builder<List<CmdParam>, List<CmdParam>>()
					.worker(joinWorker)
					.callback(joinWorker)
					.build();
			// 如果所有cmd已完成,ifFinished=true，则返回EndCmd
			// 如果返回的是最后一个，则返回currentWorker+endCmd
			// 如果不是最后一个，则返回currentWorker+joinCmd
			WorkerWrapper<?,?>[] currentWorkerWWrappers = buildWorker(rootCmdInstanceVo, waybillInstanceVo);

			CmdInstanceWorker endWorker = new CmdInstanceWorker("", CmdType.EndCmd, CmdInstanceContext.builder().waybillInstanceId(waybillInstanceId).build().setWaybillInstanceVo(waybillInstanceVo));
			WorkerWrapper<List<CmdParam>, List<CmdParam>> endWorkerWrapper = new WorkerWrapper.Builder<List<CmdParam>, List<CmdParam>>()
					.worker(endWorker)
					.callback(endWorker)
					.build();
			if (currentWorkerWWrappers == null) {
				return new WorkerWrapper[] { endWorkerWrapper };
			} else {
				for (WorkerWrapper<?, ?> currentWorkerWWrapper : currentWorkerWWrappers) {
					joinWorkerWrapper.addDepend(currentWorkerWWrapper, true);
					currentWorkerWWrapper.getNextWrappers().add(joinWorkerWrapper);
				}
				return currentWorkerWWrappers;
			}
		}
		return null;
	}

	private static WorkerWrapper<?, ?>[] buildWorker(CmdInstanceVo cmdInstanceVo, WaybillInstanceVo waybillInstanceVo) {
		String waybillInstanceId = waybillInstanceVo.getId();
		Stack<CmdInstanceVo> stack = new Stack<>();
		stack.push(cmdInstanceVo);
		while (!stack.isEmpty()) {
			CmdInstanceVo current = stack.pop();
			// 访问根节点
			if (current.getCode() == null) {
				// 返回,当前节点尚未处理
				CmdInstanceWorker worker = new CmdInstanceWorker(current.getId(), current.getCmdType(),
						CmdInstanceContext.builder().waybillInstanceId(waybillInstanceId).cmdInstanceId(current.getId()).cmdId(current.getCmd().getId()).lastAgvMsg(current.findChildAgvMsg(current.getId())).build().setWaybillInstanceVo(waybillInstanceVo));
				WorkerWrapper<?, ?> workerWrapper = new WorkerWrapper.Builder<List<CmdParam>, List<CmdParam>>()
						.worker(worker)
						.callback(worker)
						.param(buildInputParams(current.getInputParams(), waybillInstanceVo))
						.build();
				return new WorkerWrapper[] { workerWrapper };
			} else {
				List<CmdInstanceVo> children = current.getChildren();
				if (current.getCmdType().equals(CmdType.ConcurrentCmd) && CollUtil.isNotEmpty(children)) {
					boolean ifConcurrent = children.stream().anyMatch(x -> x.getCode() == null);
					if (ifConcurrent) {
						WorkerWrapper<?, ?>[] workerWrappers = new WorkerWrapper[children.size()];
						for (int i = 0; i < children.size(); i++) {
							CmdInstanceVo child = children.get(i);
							CmdInstanceWorker worker = new CmdInstanceWorker(child.getId(), child.getCmdType(),
									CmdInstanceContext.builder().waybillInstanceId(waybillInstanceId).cmdInstanceId(child.getId()).cmdId(child.getCmd().getId()).lastAgvMsg(child.findChildAgvMsg(child.getId())).build().setWaybillInstanceVo(waybillInstanceVo));
							WorkerWrapper<?, ?> workerWrapper = new WorkerWrapper.Builder<List<CmdParam>, List<CmdParam>>()
									.worker(worker)
									.callback(worker)
									.param(buildInputParams(child.getInputParams(), waybillInstanceVo))
									.build();
							workerWrappers[i] = workerWrapper;
						}
						return workerWrappers;
					}
				}

				if (CollUtil.isNotEmpty(children)) {
					// 栈是后进先出的
					children.stream().sorted(Comparator.comparingInt(CmdInstanceVo::getOrder).reversed()).forEach(stack::push);
				}
			}
		}
		return null;
	}

	private static List<CmdParam> buildInputParams(List<CmdParam> inputParams, WaybillInstanceVo waybillInstanceVo) {
		inputParams.forEach(cmdParam -> {
			Map<String, CmdParam> map = waybillInstanceVo.getExecutorOutputParams();
			String key = cmdParam.getKey();
			if (cmdParam.getCmdParamType().equals(CmdParamType.Expression) && map.containsKey(key)) {
				cmdParam.setEvaluatedValue(map.get(key).getValue());
			}
		});
		return inputParams;
	}
}
